﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using System.Collections.Generic;
using Tesla.Collections;
using Tesla.Content;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Util;

namespace Tesla.Scene {
    /// <summary>
    /// MeshData represents the raw geometric data of a Mesh. This class has built in properties for common vertex elements, however custom vertex elements can
    /// be added. When a MeshData constructs a vertex buffer, it will sort the vertex elements in the order of their VertexSemantic enumeration and their semantic index. 
    /// Keep this in mind when writing shaders, to ensure vertex input layouts match.
    /// 
    /// The MeshData contains a CPU copy of all data, which it uses when constructing the vertex/index buffers. This data can be used to access geometry
    /// without having to read from the graphics device.
    /// 
    /// Calling Reconstruct() will completely rebuild the vertex/index buffers and dispose of previously held resources. Do not do this per-frame. MeshData supports
    /// dynamic buffers, and once the buffers are constructed UpdateVertex/IndexData can be used to set data without recreating resources.
    /// </summary>
    public class MeshData : ISavable {
        private VertexBuffer _vertexBuffer;
        private IndexBuffer _indexBuffer;

        private Dictionary<TwoTupleKey<VertexSemantic, int>, DataBuffer> _bufferMap;
        private DataBuffer _indices;
        private PrimitiveType _primitiveType;
        private int _vertexCount;
        private int _primitiveCount;

        private bool _useIndexedPrimitives;
        private bool _useDynamicVB;
        private bool _useDynamicIB;
        private bool _vbDirty;
        private bool _ibDirty;

        /// <summary>
        /// Gets the VertexBuffer contained in this mesh data.
        /// </summary>
        public VertexBuffer VertexBuffer {
            get {
                return _vertexBuffer;
            }
        }

        /// <summary>
        /// Gets the IndexBuffer contained in this mesh data, if it uses indexed primitives.
        /// </summary>
        public IndexBuffer IndexBuffer {
            get {
                return _indexBuffer;
            }
        }

        /// <summary>
        /// Gets or sets the vertex positions (Semantic: Position, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector3> Positions {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Position, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector3)) {
                        return (DataBuffer<Vector3>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Position, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex normals (Semantic: Normal, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector3> Normals {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Normal, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector3)) {
                        return (DataBuffer<Vector3>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Normal, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex colors (Semantic: Color, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Color> Colors {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Color, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Color)) {
                        return (DataBuffer<Color>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Color, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }


        /// <summary>
        /// Gets or sets the vertex texture coordinates (Semantic: TextureCoordinate, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector2> TextureCoordinates {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.TextureCoordinate, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector2)) {
                        return (DataBuffer<Vector2>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.TextureCoordinate, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex binormals (Semantic: Binormal, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector3> Binormals {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Binormal, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector3)) {
                        return (DataBuffer<Vector3>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Binormal, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex tangents (Semantic: Tangent, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector3> Tangents {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Tangent, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector3)) {
                        return (DataBuffer<Vector3>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.Tangent, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex blend indices (Semantic: BlendIndices, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector4> BlendIndices {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.BlendIndices, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector4)) {
                        return (DataBuffer<Vector4>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.BlendIndices, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets or sets the vertex blend weights (Semantic: BlendWeight, SemanticIndex: 0)
        /// </summary>
        public DataBuffer<Vector4> BlendWeights {
            get {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.BlendWeight, 0);
                DataBuffer db;
                if(_bufferMap.TryGetValue(key, out db)) {
                    if(db != null && db.ElementType == typeof(Vector4)) {
                        return (DataBuffer<Vector4>) db;
                    } else {
                        return null;
                    }
                } else {
                    return null;
                }
            }
            set {
                TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(VertexSemantic.BlendWeight, 0);
                if(value == null) {
                    RemoveBuffer(key.First, key.Second);
                } else {
                    _bufferMap[key] = value;
                }
                _vbDirty = true;
            }
        }

        /// <summary>
        /// Gets the number of vertex element buffers contained in the mesh data.
        /// </summary>
        public int BufferCount {
            get {
                return _bufferMap.Count;
            }
        }

        /// <summary>
        /// Gets or sets the mesh data's indices. If set, UseIndexedPrimitives
        /// is automatically set.
        /// </summary>
        public DataBuffer Indices {
            get {
                return _indices;
            }
            set {
                if(value == null) {
                    _indices = null;
                    _useIndexedPrimitives = false;
                    return;
                }
                Type elemType = value.ElementType;
                if(elemType == typeof(short) || elemType == typeof(int)) {
                    _useIndexedPrimitives = true;
                    _indices = value;
                } else {
                    throw new TeslaException("Invalid index type, must be of type int or short.");
                }
                _ibDirty = true;
            }
        }

        /// <summary>
        /// Gets if the mesh data is using 16-bit indices, otherwise they are 32-bit.
        /// </summary>
        public bool UsingShortIndices {
            get {
                if(_indices != null && _indices.ElementType == typeof(short)) {
                    return true;
                }
                return false;
            }
        }

        /// <summary>
        /// Gets or sets the geometry primitive type.
        /// </summary>
        public PrimitiveType PrimitiveType {
            get {
                return _primitiveType;
            }
            set {
                if(_primitiveType != value) {
                    _primitiveType = value;
                    _vbDirty = true;
                    _ibDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets the overall vertex count.
        /// </summary>
        public int VertexCount {
            get {
                return _vertexCount;
            }
        }

        /// <summary>
        /// Gets the overall index count.
        /// </summary>
        public int IndexCount {
            get {
                return _indices.Length;
            }
        }

        /// <summary>
        /// Gets the overall primitive count.
        /// </summary>
        public int PrimitiveCount {
            get {
                return _primitiveCount;
            }
        }

        /// <summary>
        /// Gets or sets if the geometry should be indexed data.
        /// </summary>
        public bool UseIndexedPrimitives {
            get {
                return _useIndexedPrimitives;
            }
            set {
                if(_useIndexedPrimitives != value) {
                    _useIndexedPrimitives = value;
                    _ibDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets or sets if the geometry should use a dynamic vertex buffer.
        /// </summary>
        public bool UseDynamicVertexBuffer {
            get {
                return _useDynamicVB;
            }
            set {
                if(_useDynamicVB != value) {
                    _useDynamicVB = value;
                    _vbDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets or sets if the geometry should use a dynamic index buffer.
        /// </summary>
        public bool UseDynamicIndexBuffer {
            get {
                return _useDynamicIB;
            }
            set {
                if(_useDynamicIB != value) {
                    _useDynamicIB = value;
                    _ibDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets if the vertex buffer requires to be reconstructed, due to
        /// data change.
        /// </summary>
        public bool VertexBufferDirty {
            get {
                return _vbDirty;
            }
        }

        /// <summary>
        /// Gets if the index buffer requires to be reconstructed, due to
        /// data change.
        /// </summary>
        public bool IndexBufferDirty {
            get {
                return _ibDirty;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MeshData"/> class.
        /// </summary>
        public MeshData() {
            _bufferMap = new Dictionary<TwoTupleKey<VertexSemantic, int>, DataBuffer>();
            _primitiveType = PrimitiveType.TriangleList;
            _useIndexedPrimitives = false;
            _useDynamicIB = false;
            _useDynamicVB = false;
            _vbDirty = true;
            _ibDirty = true;
            _primitiveCount = 0;
            _vertexCount = 0;
        }

        /// <summary>
        /// Adds a vertex element to the mesh data's buffer map. Each of these buffers correspond to
        /// all the data for a specific vertex element. This will always set the semantic index to 0.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <param name="buffer">Vertex element data</param>
        public void AddBuffer(VertexSemantic semantic, DataBuffer buffer) {
            AddBuffer(semantic, 0, buffer);
        }

        /// <summary>
        /// Adds a vertex element to the mesh data's buffer map. Each of these buffers correspond to
        /// all the data for a specific vertex element.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <param name="index">Vertex semantic index</param>
        /// <param name="buffer">Vertex element data</param>
        public void AddBuffer(VertexSemantic semantic, int index, DataBuffer buffer) {
            if(!ValidType(buffer.ElementType)) {
                throw new TeslaException("Invalid vertex type! Refer to VertexElementFormat enum for valid types.");
            }

            TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(semantic, index);
            if(buffer == null) {
                if(_bufferMap.ContainsKey(key)) {
                    _bufferMap.Remove(key);
                }
            } else {
                _bufferMap[key] = buffer;
            }

            _vbDirty = true;
        }

        private bool ValidType(Type type) {
            if(type == typeof(float)) {
                return true;
            } else if(type == typeof(Color)) {
                return true;
            } else if(type == typeof(Vector2)) {
                return true;
            } else if(type == typeof(Vector3)) {
                return true;
            } else if(type == typeof(Vector3)) {
                return true;
            } else if(type == typeof(Quaternion)) {
                return true;
            } else if(type == typeof(Vector4)) {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Removes a vertex element from the mesh data's buffer map. This will
        /// always remove the buffer associated with the vertex semantic and semantic index of 0.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <returns>The buffer removed</returns>
        public DataBuffer RemoveBuffer(VertexSemantic semantic) {
            return RemoveBuffer(semantic, 0);
        }

        /// <summary>
        /// Removes a vertex element from the mesh data's buffer map.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <param name="index">The semantic index</param>
        /// <returns>The buffer removed, if any</returns>
        public DataBuffer RemoveBuffer(VertexSemantic semantic, int index) {
            TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(semantic, index);
            DataBuffer toReturn;
            if(_bufferMap.TryGetValue(key, out toReturn)) {
                _bufferMap.Remove(key);
                _vbDirty = true;
                return toReturn;
            }
            return null;
        }

        /// <summary>
        /// Gets a vertex element buffer from the mesh data's buffer map. This will always be the vertex element
        /// with semantic index of 0.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <returns>The vertex element buffer, if it exists</returns>
        public DataBuffer GetBuffer(VertexSemantic semantic) {
            return GetBuffer(semantic, 0);
        }

        /// <summary>
        /// Gets a vertex element buffer from the mesh data's buffer map.
        /// </summary>
        /// <param name="semantic">Vertex semantic</param>
        /// <param name="index">Semantic index</param>
        /// <returns>The vertex element buffer, if it exists</returns>
        public DataBuffer GetBuffer(VertexSemantic semantic, int index) {
            TwoTupleKey<VertexSemantic, int> key = new TwoTupleKey<VertexSemantic, int>(semantic, index);
            DataBuffer toReturn;
            if(_bufferMap.TryGetValue(key, out toReturn)) {
                return toReturn;
            }
            return null;
        }

        /// <summary>
        /// Queries if the mesh data's buffer map contains the vertex element buffer that
        /// corresponds to the specified semantic and semantic index of 0.
        /// </summary>
        /// <param name="semantic">Vertex Semantic</param>
        /// <returns>True if the vertex element buffer exists, false otherwise</returns>
        public bool ContainsBuffer(VertexSemantic semantic) {
            return ContainsBuffer(semantic, 0);
        }

        /// <summary>
        /// Queries if the mesh data's buffer map contains the vertex element buffer that
        /// corresponds to the specified semantic and semantic index.
        /// </summary>
        /// <param name="semantic">Vertex Semantic</param>
        /// <param name="index">Semantic index</param>
        /// <returns>True if the vertex element buffer exists, false otherwise</returns>
        public bool ContainsBuffer(VertexSemantic semantic, int index) {
            return _bufferMap.ContainsKey(new TwoTupleKey<VertexSemantic, int>(semantic, index));
        }

        /// <summary>
        /// Clears all data from the mesh data and invalidates vertex/index buffers.
        /// </summary>
        public void ClearData() {
            _bufferMap.Clear();
            _indices = null;
            _vbDirty = true;
            _ibDirty = true;
            if(_vertexBuffer != null) {
                _vertexBuffer.Dispose();
                _vertexBuffer = null;
            }

            if(_indexBuffer != null) {
                _indexBuffer = null;
            }
        }

        /// <summary>
        /// Translates position vertex data by adding the specified translation. If the vertex buffer has 
        /// been constructed, this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="x">X value to add</param>
        /// <param name="y">Y value to add</param>
        /// <param name="z">Z value to add</param>
        public void TranslatePositions(float x, float y, float z) {
            TranslatePositions(new Vector3(x, y, z));
        }

        /// <summary>
        /// Translates position vertex data. If the vertex buffer has been constructed,
        /// this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="translation">Value to add</param>
        public void TranslatePositions(Vector3 translation) {
            DataBuffer<Vector3> pos = this.Positions;
            if(pos != null) {
                pos.Position = 0;
                for(int i = 0; i < pos.Length; i++) {
                    Vector3 p = pos.Get(i);
                    Vector3.Add(ref p, ref translation, out p);
                    pos.Set(p);
                }
                UpdateVertexData<Vector3>(VertexSemantic.Position, pos);
            }
        }

        /// <summary>
        /// Transforms each vertex position by the specified SRT transformation. If the vertex buffer 
        /// has been constructed, this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="transform">SRT transform</param>
        public void TransformPositions(Transform transform) {
            DataBuffer<Vector3> pos = this.Positions;
            if(pos != null) {
                pos.Position = 0;
                for(int i = 0; i < pos.Length; i++) {
                    Vector3 p = pos.Get(i);
                    transform.TransformVector(ref p, out p);
                    pos.Set(p);
                }
                UpdateVertexData<Vector3>(VertexSemantic.Position, pos);
            }
        }

        /// <summary>
        /// Transforms each vertex position by the specified matrix transformation. If the vertex buffer 
        /// has been constructed, this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="transform">matrix transform</param>
        public void TransformPositions(Matrix transform) {
            DataBuffer<Vector3> pos = this.Positions;
            if(pos != null) {
                pos.Position = 0;
                for(int i = 0; i < pos.Length; i++) {
                    Vector3 p = pos.Get(i);
                    Vector3 result;
                    Vector3.Transform(ref p, ref transform, out result);
                    pos.Set(result);
                }
                UpdateVertexData<Vector3>(VertexSemantic.Position, pos);
            }
        }

        /// <summary>
        /// Transforms each vertex normal by the specified Scale-Rotation (SR) transform. If the vertex buffer
        /// has been constructed, this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="transform">Scale-Rotate transform, scale expected to be uniform</param>
        /// <param name="normalize">True if vector should be normalized</param>
        public void TransformNormals(Transform transform, bool normalize) {
            DataBuffer<Vector3> norms = this.Positions;
            if(norms != null) {
                norms.Position = 0;
                for(int i = 0; i < norms.Length; i++) {
                    Vector3 n = norms.Get(i);
                    transform.TransformVector(ref n, out n);
                    if(normalize) {
                        n.Normalize();
                    }
                    norms.Set(n);
                }
                UpdateVertexData<Vector3>(VertexSemantic.Normal, norms);
            }
        }

        /// <summary>
        /// Transforms each vertex normal by the specified Scale-Rotation (SR) matrix. If the vertex buffer
        /// has been constructed, this updates that, otherwise it just updates the CPU copy.
        /// </summary>
        /// <param name="transform">Scale-Rotate transform, scale expected to be uniform</param>
        /// <param name="normalize">True if vector should be normalized</param>
        public void TransformNormals(Matrix transform, bool normalize) {
            DataBuffer<Vector3> norms = this.Positions;
            if(norms != null) {
                norms.Position = 0;
                for(int i = 0; i < norms.Length; i++) {
                    Vector3 n = norms.Get(i);
                    Vector3 result;
                    Vector3.Transform(ref n, ref transform, out result);
                    if(normalize) {
                        result.Normalize();
                    }
                    norms.Set(result);
                }
                UpdateVertexData<Vector3>(VertexSemantic.Normal, norms);
            }
        }

        /// <summary>
        /// Gets the vertex position for the primitive specified by its
        /// index in the mesh. Triangles return a list of 3 points and lines of 2 points.
        /// </summary>
        /// <param name="primitiveIndex">Index of the primitive in the mesh</param>
        /// <exception cref="Tesla.Core.TeslaException">Throws if primitiveIndex is out of range or if indices
        /// are not set, but used</exception>
        public Vector3[] GetPrimitive(int primitiveIndex) {
            int rsize = 0;
            switch(_primitiveType) {
                case PrimitiveType.TriangleList:
                case PrimitiveType.TriangleStrip:
                    rsize = 3;
                    break;
                case PrimitiveType.LineList:
                case PrimitiveType.LineStrip:
                    rsize = 2;
                    break;
            }
            Vector3[] store = new Vector3[rsize];
            GetPrimitive(primitiveIndex, store);
            return store;
        }

        /// <summary>
        /// Gets the vertex position for the primitive specified by its
        /// index in the mesh. Triangles return a list of 3 points, and lines of 2 points.
        /// </summary>
        /// <param name="primitiveIndex">Index of the primitive in the mesh</param>
        /// <param name="store">Array to store the primitive vertices in. For triangle lists/strips, must be size 3. For line lists/strips must be size 2.</param>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if primitiveIndex is out of range or if indices
        /// are not set, but used</exception>
        public void GetPrimitive(int primitiveIndex, Vector3[] store) {
            if(primitiveIndex >= _primitiveCount || primitiveIndex < 0) {
                throw new TeslaException("primitiveIndex", "PrimitiveIndex out of range");
            }

            if(this.Positions == null || (_useIndexedPrimitives && _indices == null)) {
                throw new TeslaException("MeshData is using indices, but no indices are set!");
            }

            int rsize = 0;
            switch(_primitiveType) {
                case PrimitiveType.TriangleList:
                case PrimitiveType.TriangleStrip:
                    rsize = 3;
                    if(store.Length != 3) {
                        throw new TeslaException("Array to store primitive is not of proper size");
                    }
                    break;
                case PrimitiveType.LineList:
                case PrimitiveType.LineStrip:
                    rsize = 2;
                    if(store.Length != 2) {
                        throw new TeslaException("Array to store primitive is not of proper size");
                    }
                    break;
            }
            DataBuffer<Vector3> pos = this.Positions;
            for(int i = 0; i < rsize; i++) {
                if(_useIndexedPrimitives) {
                    if(this.UsingShortIndices) {
                        store[i] = pos.Get(((DataBuffer<short>) _indices).Get(GetVertexIndex(primitiveIndex, i)));
                    } else {
                        store[i] = pos.Get(((DataBuffer<int>) _indices).Get(GetVertexIndex(primitiveIndex, i)));
                    }
                } else {
                    store[i] = pos.Get(GetVertexIndex(primitiveIndex, i));
                }
            }
        }

        /// <summary>
        /// Given the primitive index in this mesh (e.g. the first, second, third triangle,
        /// and so on), return an index array of their vertex information. Supports all
        /// the PrimitiveTypes (so triangles will return 3 points and lines 2 points), as well
        /// as indexed/non-indexed vertex buffers.
        /// 
        /// Note, if short indices are used they will be returned as Int32's.
        /// </summary>
        /// <param name="primitiveIndex">Primitive position in the mesh</param>
        /// <returns>Index information for that primitive, null if index was out of bounds</returns>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if primitiveIndex is out of range or if indices are not set</exception>
        public int[] GetPrimitiveIndices(int primitiveIndex) {
            int rsize = 0;
            switch(_primitiveType) {
                case PrimitiveType.TriangleList:
                case PrimitiveType.TriangleStrip:
                    rsize = 3;
                    break;
                case PrimitiveType.LineList:
                case PrimitiveType.LineStrip:
                    rsize = 2;
                    break;
            }

            int[] store = new int[rsize];
            GetPrimitiveIndices(primitiveIndex, store);
            return store;
        }

        /// <summary>
        /// Given the primitive index in this mesh (e.g. the first, second, third triangle,
        /// and so on), return an index array of their vertex information. Supports all
        /// the PrimitiveTypes (so triangles will return 3 points and lines 2 points), as well
        /// as indexed/non-indexed vertex buffers.
        /// 
        /// Note, if short indices are used they will be returned as Int32's.
        /// </summary>
        /// <param name="primitiveIndex">Primitive position in the mesh</param>
        /// <param name="store">Array to store primitive indices in</param>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if primitiveIndex is out of range or if indices are not set</exception>
        public void GetPrimitiveIndices(int primitiveIndex, int[] store) {
            if(primitiveIndex >= _primitiveCount || primitiveIndex < 0) {
                throw new TeslaException("primitiveIndex", "PrimitiveIndex out of range");
            }

            if(this.Positions == null || (_useIndexedPrimitives && _indices == null)) {
                throw new TeslaException("MeshData is using indices, but no indices are set!");
            }

            int rsize = 0;
            switch(_primitiveType) {
                case PrimitiveType.TriangleList:
                case PrimitiveType.TriangleStrip:
                    rsize = 3;
                    if(store.Length != rsize) {
                        throw new TeslaException("Storing array must be the size of a triangle (3).");
                    }
                    break;
                case PrimitiveType.LineList:
                case PrimitiveType.LineStrip:
                    rsize = 2;
                    if(store.Length != rsize) {
                        throw new TeslaException("Storing array must be the size of a line (2).");
                    }
                    break;
            }

            for(int i = 0; i < rsize; i++) {
                if(_useIndexedPrimitives) {
                    if(this.UsingShortIndices) {
                        store[i] = ((DataBuffer<short>) _indices).Get(GetVertexIndex(primitiveIndex, i));
                    } else {
                        store[i] = ((DataBuffer<int>) _indices).Get(GetVertexIndex(primitiveIndex, i));
                    }
                } else {
                    store[i] = GetVertexIndex(primitiveIndex, i);
                }
            }
        }

        /// <summary>
        /// Given the position of a primitive in the mesh, and an index, get the
        /// indexing used for that primitive's vertex. E.g. for triangles, the pointIndex
        /// index would be 0-2, lines 0-1, points 0.
        /// </summary>
        /// <param name="primitiveIndex">Primitive position in this mesh</param>
        /// <param name="pointIndex">Vertex index</param>
        /// <returns>Index for this vertex, -1 if there was an error</returns>
        public int GetVertexIndex(int primitiveIndex, int pointIndex) {
            int index = 0;

            switch(_primitiveType) {
                case PrimitiveType.TriangleList:
                    index = (primitiveIndex * 3) + pointIndex;
                    break;
                case PrimitiveType.TriangleStrip:
                    index = primitiveIndex + pointIndex;
                    break;
                case PrimitiveType.LineList:
                    index = (primitiveIndex * 2) + pointIndex;
                    break;
                case PrimitiveType.LineStrip:
                    index = primitiveIndex + pointIndex;
                    break;
            }

            return index;
        }

        /// <summary>
        /// Constructs the vertex and index buffers according to the vertex element buffers and index
        /// information the mesh data contains at the time of the reconstruction. This typically is done
        /// when the geometry is first being set up or if new vertex elements are added. Updating existing
        /// data should be done via the UpdateVertexData and UpdateIndexData methods, as these allow for
        /// data to be changed without reconstructing hardware resources.
        /// </summary>
        public void Reconstruct() {
            UpdateVertexCount();
            UpdatePrimitiveCount();
            if(_useIndexedPrimitives) {
                ConstructIndexBuffer();
            }
            ConstructVertexBuffer();
        }

        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="data">The vertex data.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, DataBuffer<T> data) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, 0, data, 0, data.Length, DataWriteOptions.None);
        }

        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="startIndex">The starting index in the data to read from..</param>
        /// <param name="elementCount">The number of elements to write.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, DataBuffer<T> data, int startIndex, int elementCount) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, 0, data, startIndex, elementCount, DataWriteOptions.None);
        }

        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="startVertex">The starting vertex in the vertex buffer at which to write.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="startIndex">The starting index in the data to read from..</param>
        /// <param name="elementCount">The number of elements to write.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, int startVertex, DataBuffer<T> data, int startIndex, int elementCount) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, startVertex, data, startIndex, elementCount, DataWriteOptions.None);
        }


        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="options">The write options for dynamic vertex buffers.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, DataBuffer<T> data, DataWriteOptions options) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, 0, data, 0, data.Length, options);
        }

        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="startIndex">The starting index in the data to read from..</param>
        /// <param name="elementCount">The number of elements to write.</param>
        /// <param name="options">The write options for dynamic vertex buffers.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, DataBuffer<T> data, int startIndex, int elementCount, DataWriteOptions options) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, 0, data, startIndex, elementCount, options);
        }


        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="startVertex">The starting vertex in the vertex buffer at which to write.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="startIndex">The starting index in the data to read from..</param>
        /// <param name="elementCount">The number of elements to write.</param>
        /// <param name="options">The write options for dynamic vertex buffers.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer. due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, int startVertex, DataBuffer<T> data, int startIndex, int elementCount, DataWriteOptions options) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateVertexData<T>(semantic, 0, startVertex, data, startIndex, elementCount, options);
        }

        /// <summary>
        /// Updates the MeshData's vertex data. This applies to both the CPU copy and the GPU (VertexxBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid (either did not exist with an
        /// already constructed vertex buffer, or type mismatch)
        /// </summary>
        /// <typeparam name="T">Type parameter specifying vertex element type</typeparam>
        /// <param name="semantic">Vertex semantic.</param>
        /// <param name="semanticIndex">Vertex semantic index.</param>
        /// <param name="startVertex">The starting vertex in the vertex buffer at which to write.</param>
        /// <param name="data">The vertex data.</param>
        /// <param name="startIndex">The starting index in the data to read from..</param>
        /// <param name="elementCount">The number of elements to write.</param>
        /// <param name="options">The write options for dynamic vertex buffers.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentException">Thrown if the vertex element data to write does not match with an existing type in the buffer due to type mismatch or data size</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if number of elements to write will be out of range.</exception>
        public void UpdateVertexData<T>(VertexSemantic semantic, int semanticIndex, int startVertex, DataBuffer<T> data, int startIndex, int elementCount, DataWriteOptions options) where T : struct {
            //Do our checks
            if(data == null || data.Length == 0) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            MemoryHelper.CheckBounds(0, data.Length, startIndex, elementCount);

            //Try and find if the type of data already exists
            DataBuffer oldData = GetBuffer(semantic, semanticIndex);

            if(oldData != null && _vertexBuffer != null && !_vertexBuffer.IsDisposed) {
                //Ensure the new data matches the old data - if its new data, types, or length are different, 
                //then we won't have room in the vertex buffer
                if(oldData.ElementType != data.ElementType) {
                    throw new ArgumentException("Cannot update the vertex data, the vertex data type does not match the existing element type.");
                } 

                //Update the old buffer's data.
                if(data != oldData) {
                    MemoryHelper.CheckBounds(0, oldData.Length, startIndex, elementCount);
                    oldData.Position = startIndex;
                    ((DataBuffer<T>) oldData).Set(data.Buffer, startIndex, elementCount);
                }

                //Update VB
                //Ensure right size
                int vertCount = _vertexBuffer.VertexCount;
                if(elementCount + startVertex > vertCount ) {
                    throw new ArgumentOutOfRangeException("data", "Elements to write exceeds vertex count");
                }

                //Find the vertex element in the declaration
                VertexElement ve = new VertexElement();
                int byteOffset = 0;
                bool found = false;
                VertexDeclaration decl = _vertexBuffer.VertexDeclaration;
                for(int i = 0; i < decl.ElementCount; i++) {
                    ve = decl[i];
                    if(ve.SemanticName == semantic && ve.SemanticIndex == semanticIndex) {
                        found = true;
                        byteOffset = ve.Offset;
                        break;
                    }
                }

                //Do some error checking...
                if(!found) {
                    throw new ArgumentException("Cannot update vertex buffer with vertex element type that is not contained in the vertex buffer to begin with.");
                }

                int veSize = VertexDeclaration.GetVertexElementSize(ve.Format);
                if(veSize != data.ElementSizeInBytes) {
                    throw new ArgumentException("data", "Element byte size does not match with expected element byte size.");
                }

                int vertexStride = decl.VertexStride;
                int startOffset = (startVertex * vertexStride) + byteOffset;
                //Lets update the VB
                if(_vertexBuffer.BufferUsage == ResourceUsage.Static) {
                    _vertexBuffer.SetData<T>(data.Buffer, startIndex, elementCount, startOffset, vertexStride);
                } else {
                    if(options == DataWriteOptions.None) {
                        if(elementCount == data.Length) {
                            options = DataWriteOptions.Discard;
                        } 
                    }
                    _vertexBuffer.SetData<T>(data.Buffer, startIndex, elementCount, startOffset, vertexStride, options);
                }
            } else {
                if(oldData != data) {
                    if(oldData != null && oldData.ElementType == data.ElementType) {
                        //Update the old buffer's data.
                        MemoryHelper.CheckBounds(0, oldData.Length, startIndex, elementCount);
                        oldData.Position = startIndex;
                        ((DataBuffer<T>) oldData).Set(data.Buffer, startIndex, elementCount);
                        _vbDirty = true;
                    } else {
                        AddBuffer(semantic, semanticIndex, data);
                    }
                }
            }
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="data">Data to write</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null or if the data type is not valid.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(DataBuffer<T> data) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateIndexData<T>(0, data, 0, data.Length, DataWriteOptions.None);
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="data">Data to write</param>
        /// <param name="startIndex">Starting index in the Data to read from</param>
        /// <param name="elementCount">Number of elements to write</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null or if the data type is not valid.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(DataBuffer<T> data, int startIndex, int elementCount) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateIndexData<T>(0, data, startIndex, elementCount, DataWriteOptions.None);
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="start">Starting index in the IndexBuffer to write to</param>
        /// <param name="data">Data to write</param>
        /// <param name="startIndex">Starting index in the Data to read from</param>
        /// <param name="elementCount">Number of elements to write</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null or if the data type is not valid.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(int start, DataBuffer<T> data, int startIndex, int elementCount) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateIndexData<T>(start, data, startIndex, elementCount, DataWriteOptions.None);
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="data">Data to write</param>
        /// <param name="options">Write options for dynamic index buffer.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null or if the data type is not valid.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(DataBuffer<T> data, DataWriteOptions options) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateIndexData<T>(0, data, 0, data.Length, options);
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="data">Data to write</param>
        /// <param name="startIndex">Starting index in the Data to read from</param>
        /// <param name="elementCount">Number of elements to write</param>
        /// <param name="options">Write options for dynamic index buffer.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null or if the data type is not valid.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(DataBuffer<T> data, int startIndex, int elementCount, DataWriteOptions options) where T : struct {
            if(data == null) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            UpdateIndexData<T>(0, data, startIndex, elementCount, options);
        }

        /// <summary>
        /// Updates the MeshData's index data. This applies to both the CPU copy and the GPU (IndexBuffer) copy, if
        /// the latter exists. Error will occur trying to set data that is not valid for index type already
        /// contained in the IndexBuffer.
        /// </summary>
        /// <typeparam name="T">Data type - int or short.</typeparam>
        /// <param name="start">Starting index in the IndexBuffer to write to</param>
        /// <param name="data">Data to write</param>
        /// <param name="startIndex">Starting index in the Data to read from</param>
        /// <param name="elementCount">Number of elements to write</param>
        /// <param name="options">Write options for dynamic index buffer.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if the data is null</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if writing to or from the buffers will be out of range.</exception>
        public void UpdateIndexData<T>(int start, DataBuffer<T> data, int startIndex, int elementCount, DataWriteOptions options) where T : struct {
            //Do our checks
            if(data == null || data.Length == 0) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }
            MemoryHelper.CheckBounds(0, data.Length, startIndex, elementCount);

            //Get the old index data
            DataBuffer oldData = Indices;
            
            //If the index buffer was constructed, then we want to ensure the types match, and if it not we can replace the types
            if(oldData != null && _indexBuffer != null && !_indexBuffer.IsDisposed) {

                int indexCount = _indexBuffer.IndexCount;
                if(elementCount + start > indexCount) {
                    throw new ArgumentOutOfRangeException("data", "Elements to write exceeds index count");
                }

                //Ensure the new data matches the old data - if its new data, types, or length are different, 
                //then we won't have room in the vertex buffer
                if(oldData.ElementType != data.ElementType) {
                    throw new ArgumentException("Cannot update the index data, the index data type does not match the existing element type.");
                } 

                if(oldData != data) {
                    //Update the old buffer's data.
                    MemoryHelper.CheckBounds(0, Indices.Length, startIndex, elementCount);
                    Indices.Position = startIndex;
                    ((DataBuffer<T>) oldData).Set(data.Buffer, startIndex, elementCount);
                }

                //Now set the data
                if(_indexBuffer.BufferUsage == ResourceUsage.Static) {
                    _indexBuffer.SetData<T>(data.Buffer);
                } else {
                    if(options == DataWriteOptions.None) {
                        if(elementCount == data.Length) {
                            options = DataWriteOptions.Discard;
                        }
                    }
                    _indexBuffer.SetData<T>(data.Buffer, options);
                }
            } else {
                if(oldData != data) {
                    if(oldData != null && oldData.ElementType == data.ElementType) {
                        //Update the old buffer's data.
                        MemoryHelper.CheckBounds(0, Indices.Length, startIndex, elementCount);
                        Indices.Position = startIndex;
                        ((DataBuffer<T>) oldData).Set(data.Buffer, startIndex, elementCount);
                        _ibDirty = true;
                    } else {
                        Indices = data;
                    }
                }
            }
        }

        private void ConstructIndexBuffer() {
            ResourceUsage usage = (_useDynamicIB) ? ResourceUsage.Dynamic : ResourceUsage.Static;
            if(_indexBuffer != null) {
                _indexBuffer.Dispose();
                _indexBuffer = null;
            }

            if(_indices.ElementType == typeof(short)) {
                DataBuffer<short> db = (DataBuffer<short>) _indices;
                _indexBuffer = new IndexBuffer(db, usage);
            } else {
                DataBuffer<int> db = (DataBuffer<int>) _indices;
                _indexBuffer = new IndexBuffer(db, usage);
            }
            _ibDirty = false;
        }

        private void ConstructVertexBuffer() {
            ResourceUsage usage = (_useDynamicVB) ? ResourceUsage.Dynamic : ResourceUsage.Static;
            if(_vertexBuffer != null) {
                _vertexBuffer.Dispose();
                _vertexBuffer = null;
            }

            //TODO: Optimize...
            List<KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer>> elements = new List<KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer>>(_bufferMap);
            elements.Sort(
                delegate(KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer> first, KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer> second) {
                    return first.Key.CompareTo(second.Key);
                }
             );

            int bufferCount = elements.Count;

            VertexElement[] velems = new VertexElement[bufferCount];
            DataBuffer[] buffers = new DataBuffer[bufferCount];

            int offset = 0;
            for(int i = 0; i < bufferCount; i++) {
                KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer> kv = elements[i];
                DataBuffer db = kv.Value;
                TwoTupleKey<VertexSemantic, int> key = kv.Key;
                VertexFormat format = GetVertexFormat(db.ElementType);
                VertexSemantic semantic = key.First;
                int semanticIndex = key.Second;
                velems[i] = CreateVertexElement(semantic, format, semanticIndex, ref offset);
                buffers[i] = db;
            }

            VertexDeclaration decl = new VertexDeclaration(velems);
            _vertexBuffer = new VertexBuffer(decl, usage, buffers);

            Array.Clear(buffers, 0, buffers.Length);
            buffers = null;
            elements.Clear();
            elements = null;
            _vbDirty = false;
        }

        private VertexFormat GetVertexFormat(Type type) {
            if(type == typeof(float)) {
                return VertexFormat.Single;
            } else if(type == typeof(Color)) {
                return VertexFormat.Color;
            } else if(type == typeof(Vector2)) {
                return VertexFormat.Vector2;
            } else if(type == typeof(Vector3)) {
                return VertexFormat.Vector3;
            } else if(type == typeof(Vector4)) {
                return VertexFormat.Vector4;
            } else if(type == typeof(Quaternion)) {
                return VertexFormat.Vector4;
            }
            throw new TeslaException("Invalid buffer type, refer to VertexElementForamt enum for valid types.");
        }

        private VertexElement CreateVertexElement(VertexSemantic semantic, VertexFormat format, int semanticIndex, ref int byteOffset) {
            VertexElement element = new VertexElement(semantic, semanticIndex, format, byteOffset);
            switch(format) {
                case VertexFormat.Single:
                    byteOffset += sizeof(float);
                    break;
                case VertexFormat.Color:
                    byteOffset += Color.SizeInBytes;
                    break;
                case VertexFormat.Vector2:
                    byteOffset += Vector2.SizeInBytes;
                    break;
                case VertexFormat.Vector3:
                    byteOffset += Vector3.SizeInBytes;
                    break;
                case VertexFormat.Vector4:
                    byteOffset += Vector4.SizeInBytes;
                    break;
            }
            return element;
        }

        /// <summary>
        /// Updates the number of vertices contained in the mesh data.
        /// </summary>
        public void UpdateVertexCount() {
            DataBuffer verts = GetBuffer(VertexSemantic.Position, 0);
            if(verts == null) {
                _vertexCount = 0;
            } else {
                _vertexCount = verts.Length;
            }
        }

        /// <summary>
        /// Updates the primitive count by using either index or vertex
        /// data, depending on if indexed primitives compose the mesh data.
        /// </summary>
        public void UpdatePrimitiveCount() {
            DataBuffer positions = GetBuffer(VertexSemantic.Position, 0);

            if(_useIndexedPrimitives && _indices == null) {
                _primitiveCount = 0;
                return;
            } else if(positions == null) {
                _primitiveCount = 0;
                return;
            }

            switch(_primitiveType) {
                case PrimitiveType.TriangleStrip:
                    if(_useIndexedPrimitives) {
                        _primitiveCount = _indices.Length - 2;
                    } else {
                        _primitiveCount = positions.Length - 2;
                    }
                    break;
                case PrimitiveType.TriangleList:
                    if(_useIndexedPrimitives) {
                        _primitiveCount = _indices.Length / 3;

                    } else {
                        _primitiveCount = positions.Length / 3;
                    }
                    break;
                case PrimitiveType.LineStrip:
                    if(_useIndexedPrimitives) {
                        _primitiveCount = _indices.Length - 1;
                    } else {
                        _primitiveCount = positions.Length - 1;
                    }
                    break;
                case PrimitiveType.LineList:
                    if(_useIndexedPrimitives) {
                        _primitiveCount = _indices.Length / 2;
                    } else {
                        _primitiveCount = positions.Length / 2;
                    }
                    break;
            }
        }

        /// <summary>
        /// Serializes the object and writes it to the output.
        /// </summary>
        /// <param name="output">Savable Output</param>
        public void Write(ISavableWriter output) {
            output.Write("UseIndexedPrimitives", _useIndexedPrimitives);
            output.Write("UseDynamicVertexBuffer", _useDynamicVB);
            output.Write("UseDynamicIndexBuffer", _useDynamicIB);
            output.WriteEnum<PrimitiveType>("PrimitiveType", _primitiveType);

            output.Write("NumBuffers", _bufferMap.Count);

            foreach(KeyValuePair<TwoTupleKey<VertexSemantic, int>, DataBuffer> kv in _bufferMap) {
                output.WriteEnum<VertexSemantic>("VertexSemantic", kv.Key.First);
                output.Write("SemanticIndex", kv.Key.Second);
                output.WriteSavable<DataBuffer>("Buffer", kv.Value);
            }

            if(_useIndexedPrimitives) {
                output.WriteSavable<DataBuffer>("Indices", _indices);
            }

            bool reconstruct = _vertexBuffer != null;

            output.Write("ReconstructFlag", reconstruct);
        }

        /// <summary>
        /// Deserializes the object and populates it from the input.
        /// </summary>
        /// <param name="input">Savable input</param>
        public void Read(ISavableReader input) {
            _useIndexedPrimitives = input.ReadBoolean();
            _useDynamicVB = input.ReadBoolean();
            _useDynamicIB = input.ReadBoolean();
            _primitiveType = input.ReadEnum<PrimitiveType>();

            int count = input.ReadInt();

            for(int i = 0; i < count; i++) {
                VertexSemantic semantic = input.ReadEnum<VertexSemantic>();
                int index = input.ReadInt();
                DataBuffer db = input.ReadSavable<DataBuffer>();
                AddBuffer(semantic, index, db);
            }

            if(_useIndexedPrimitives) {
                _indices = input.ReadSavable<DataBuffer>();
            }

            bool reconstruct = input.ReadBoolean();

            if(reconstruct) {
                Reconstruct();
            }
        }
    }
}
